<?php

define('_SOAP_HOST_', 'localhost');
define('_SOAP_GI_', '/index.php/Api');
define('_SOAP_SITE_', '1');
if (function_exists('import'))
//考虑到thinkphp无法使用require_once的情况
    import('SSO/xml');
else
    require_once 'lib/xml.class.php';

$_aHttpHost = explode('.', $_SERVER['HTTP_HOST']);
$_siteSurfix = $_aHttpHost[count($_aHttpHost) - 1];
// <editor-fold defaultstate="collapsed" desc="设置全站处理需要用到的cookie键名，域名">
//永久记住用户信息的的cookie键名
define('COOKIE_FOREVER', 'forever');
//全站sid cookie键名
define('COOKIE_SID', 'sid');
//全域cookie域名
define('COOKIE_DOMAIN', '.hostname.' . $_siteSurfix);
//通信密钥
define('_CNVP_KEY', 'xxxxxx');

// </editor-fold>

class CnvpClientSSO {

    // <editor-fold defaultstate="collapsed" desc="属性定义及初始静态函数">
    //站点标志
    protected static $site = 0;
    //SOAP通信协议[http=>'';https=>'ssl://';]
    protected static $scheme = '';
    //SOAP主机地址
    protected static $host = 'hostname';
    //SOAP通信端口[http=>80;https=>443;]
    protected static $port = 80;
    //SOAP请求CGI
    protected static $cgi = '/port/testMmoUser.php';
    //SOAP请求限时
    protected static $stamp = 30;
    //本站向用户中心获取与设置全站id的链接地址
    static private $_getSessIdUrl;
    static private $_ucSessKey = '_cnvp_user';
    //与用户验证中心进行webservice请求通信的全局校验串
    static private $_mcComunicationKey = 'xxxxx';
    //本站与用户中心 通过W3C标准，来获取与设置全站唯一id的通信私钥
    static private $_authcodeKey = 'xxxxx';
    //全站登陆script串
    static private $_synloginScript = null;
    //全站登出script串
    static private $_synlogoutScript = null;
    //当前时间撮
    static private $_timestamp;
    //本站是否安装有php soap模块的标记
    static private $_hasSoapModule;

    //本静态类 部分方法需要调用的初始化方法
    private static function _init() {
        self::$_timestamp = time();
        self::$_getSessIdUrl = 'http://' . self::$host . '/port/cookie_mgr.php';
        self::$_hasSoapModule = class_exists('SoapClient') ? true : false;
        //self::_initSess();
    }

    //检测站点域名是否和用户中心域名一致，如一致而且开启了sesion，则会导致webservice调用失败
    private static function _initSess($start = false) {
        if (!$start && isset($_SESSION)) {
            session_write_close();
        } else if ($start) {
            @session_start();
        } else {
            
        }
        return;
    }

    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="登陆相关">
    //用户单点登陆验证函数
    /**
     * 用户单点登陆验证函数
     * @return type
     */
    public static function checkUserLogin() {
        self::_init();
        $ret = array();
        $_sessId = self::_getLocalSid();
        if (empty($_sessId)) {
            //永久记住账号处理
            if (isset($_COOKIE[COOKIE_FOREVER]) && !empty($_COOKIE[COOKIE_FOREVER])) {

                //3. 根据cookie里的用户名和密码判断用户是否已经登陆。
                $_userinfo = explode('|g|', self::authcode($_COOKIE[COOKIE_FOREVER], 'DECODE', self::$_authcodeKey));

                $username = $_userinfo[0];
                $password = isset($_userinfo[1]) ? $_userinfo[1] : '';
                if (empty($password)) {
                    $ret['status'] = -3;
                } else {
                    return self::loginSSO($username, $password, true, true);
                }
            } else {
                $ret['status'] = -3;
            }
        } else {
            //
            //本站原先已经登陆过，通过保留的sesson id存根去用户中心验证
            //
			$_params = array(
                'sessId' => $_sessId,
                'siteFlag' => self::$site,
                'checksum' => md5($_sessId . self::$site . self::$_mcComunicationKey)
            );
            $aRet = self::_callSoap('getOnlineUser', $_params);
            if (intval($aRet['resultFlag']) > 0) {
                //成功登陆
                $ret = $aRet['userinfo'];
            } else {
                $ret['status'] = $aRet['resultFlag'];
            }
        }

        return $ret;
    }

    static public function loginSSO($username, $password, $remember = false, $alreadyEnc = false) {
        self::_init();
        self::_removeLocalSid();
        $ret = array();
        // <editor-fold defaultstate="collapsed" desc="1. 处理传入webservice接口的参数">
        $_params = array(
            'username' => $username,
            'password' => $alreadyEnc ? self::simpleEncPass(trim($password)) : trim($password),
            'ip' => self::onlineip(),
            'siteFlag' => self::$site,
            'remember' => $remember
        );
        $_params['checksum'] = self::_getCheckSum($_params['username'] . $_params['password'] .
                        $_params['ip'] . $_params['siteFlag'] . $_params['remember']);
        // </editor-fold>
        // <editor-fold defaultstate="collapsed" desc="2.调用webservice接口，进行登陆处理">
        $aRet = self::_callSoap('loginUCenter', $_params);
        if (intval($aRet['resultFlag']) > 0 && $aRet['sessID']) {
            //成功登陆
            //设置本地session id
            self::_setLocalSid($aRet['sessID']);
            //设置用户中心的统一session id脚本路径
            self::$_synloginScript = urldecode($aRet['script']);
            $ret = $aRet['userinfo'];
        } else {
            $ret['resultFlag'] = $aRet['resultFlag'];
        }
        // </editor-fold>
        return $ret;
    }

    //获取设置全站登陆的scirpt代码（获取后，请在页面中输出）
    public static function getSynloginScript() {
        return self::$_synloginScript;
    }

    //取得用户IP
    public static function onlineip() {
        if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
            $onlineip = getenv('HTTP_CLIENT_IP');
        } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
            $onlineip = getenv('HTTP_X_FORWARDED_FOR');
        } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
            $onlineip = getenv('REMOTE_ADDR');
        } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
            $onlineip = $_SERVER['REMOTE_ADDR'];
        }
        return preg_replace("/^([\d\.]+).*/", "\\1", $onlineip);
    }

    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="退出相关">
    //全站单点登出- 通过webservice请求注销掉用户的全站唯一标识
    public static function logoutSSO() {
        self::_init();
        $_sessId = self::_getLocalSid();
        //
        //本站没有登陆的话，不让同步登出其他站
        //
		if (empty($_sessId)) {
            self::_initSess(true);
            return false;
        }
        $_params = array(
            'sessId' => $_sessId,
            'siteFlag' => self::$site,
            'checksum' => md5($_sessId . self::$site . self::$_mcComunicationKey)
        );

        $aRet = self::_callSoap('logoutUCenter', $_params);
        if (intval($aRet['resultFlag']) > 0) {
            //成功登出
            self::_removeLocalSid();  //移除本站记录的sid存根
            self::$_synlogoutScript = urldecode($aRet['script']);
            $ret = 1;
        } else {
            $ret = $aRet['resultFlag'];
        }
        return intval($ret);
    }

    //获取立即登出其他站的script代码
    public static function getSynlogoutScript() {
        return self::$_synlogoutScript;
    }

    // </editor-fold>        
    // <editor-fold defaultstate="collapsed" desc="用户相关函数">
    //用户单点登陆验证函数 + 获取用户详细资料的处理
    public static function getOnlineUserDetail($fields = null) {
        self::_init();
        $ret = array();
        $_sessId = self::_getLocalSid();
        if (empty($_sessId)) {
            //永久记住账号处理
            if (isset($_COOKIE[COOKIE_FOREVER]) && !empty($_COOKIE[COOKIE_FOREVER])) {

                //3. 根据cookie里的用户名和密码判断用户是否已经登陆。
                $_userinfo = explode('|g|', self::authcode($_COOKIE[COOKIE_FOREVER], 'DECODE', self::$_authcodeKey));

                $username = $_userinfo[0];
                $password = isset($_userinfo[1]) ? $_userinfo[1] : '';
                if (empty($password)) {
                    $ret['status'] = -3;
                } else {
                    $_loginRes = self::loginSSO($username, $password, true, true);

                    //登陆成功，重新向用户中心发送获取详细信息的请求
                    if ($_loginRes['status'] > 0) {
                        $_sessId = self::_getLocalSid();
                        if (empty($_sessId)) {
                            $ret['status'] = -3;
                        } else {
                            return self::getOnlineUserDetail($fields);
                        }
                    } else {
                        $ret['status'] = -3;
                    }
                }// end of remember's login check
            } else {
                $ret['status'] = -3;
            }
        } else {
            //
            //本站原先已经登陆过，通过保留的sesson id存根去用户中心验证
            //
			$_params = array(
                'sessId' => $_sessId,
                'siteFlag' => self::$site,
                'fields' => $fields,
                'checksum' => md5($_sessId . self::$site . self::$_mcComunicationKey)
            );
            $aRet = self::_callSoap('getOnlineUserDetail', $_params);
            if (intval($aRet['resultFlag']) > 0) {
                //成功登陆
                $ret = $aRet['userinfo'];
                $ret['status'] = 1;
            } else {
                $ret['status'] = $aRet['resultFlag'];
            }
        }

        return $ret;
    }

    public static function getOnlineList() {
        
    }

    public static function getUserCount() {
        
    }

    public static function remove() {
        
    }

    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="与用户中心通信的可逆 加密方法">
    public static function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
        $string = str_replace(' ', '+', $string);
        $ckey_length = 4;

        $key = md5($key ? $key : _CNVP_KEY);
        $keya = md5(substr($key, 0, 16));
        $keyb = md5(substr($key, 16, 16));
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';

        $cryptkey = $keya . md5($keya . $keyc);
        $key_length = strlen($cryptkey);

        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
        $string_length = strlen($string);

        $result = '';
        $box = range(0, 255);

        $rndkey = array();
        for ($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }

        for ($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }

        for ($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }

        if ($operation == 'DECODE') {
            if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            return $keyc . str_replace('=', '', base64_encode($result));
        }
    }

    // </editor-fold>	
    // <editor-fold defaultstate="collapsed" desc="设置相关参数">
    public static function simpleEncPass($pass) {
        return md5(strtolower($pass));
    }

    public static function setSite($site) {
        self::$site = intval($site);
    }

    public static function setScheme($scheme) {
        self::$scheme = ('HTTP' == strtoupper($scheme)) ? '' : $scheme;
    }

    public static function setHost($host) {
        self::$host = $host;
    }

    public static function setPort($port) {
        self::$port = $port;
    }

    public static function setCGI($cgi) {
        self::$cgi = $cgi;
    }

    public static function setStamp($stamp) {
        self::$stamp = $stamp;
    }

    // </editor-fold>
    // <editor-fold defaultstate="collapsed" desc="私有方法">     
    // 
    //用cookie方式记录 用户全站的唯一标识
    private static function _setLocalSid($sid) {
        //本地多余设置一次全域cookie，主要是兼容checkUserLogin 中的cookie记录账号 的处理
        setcookie(COOKIE_SID, $sid, null, '/', COOKIE_DOMAIN);
    }

    //获取用cookie方式记录 的 用户全站的唯一标识
    private static function _getLocalSid() {
        return isset($_COOKIE[COOKIE_SID]) ? $_COOKIE[COOKIE_SID] : '';
    }

    //获取用cookie方式记录 的 用户全站的唯一标识
    private static function _removeLocalSid() {
        setcookie(COOKIE_SID, '', -1, '/', COOKIE_DOMAIN);
//		setcookie(COOKIE_FOREVER, '', -1, '/', COOKIE_DOMAIN);
    }

    //??不明白是什么用
    private static function _getUserPassSalt($username, $password) {
        return md5($username . $password . '#@g!&yS%j');
    }

    //获取与用户中心webservice通信用的加密校验码
    private static function _getCheckSum($str, $flag = 0) {
        return md5($str . self::$_mcComunicationKey);
    }

    // <editor-fold defaultstate="collapsed" desc="SOAP相关函数">
    //简单的soap处理方法封装
    private static function _callSoap($funcName, $params = array()) {
        if (self::$_hasSoapModule) {

            $params = array_values($params);
            $_c = count($params);
            $_wsdl = (empty(self::$scheme) ? 'http://' : 'https://') . self::$host . ':' . self::$port . self::$cgi . '?wsdl';
            $_wsdl .= '&t=' . self::$_timestamp;

            try {
                $client = new SoapClient(
                                $_wsdl,
                                array('encoding' => 'UTF-8', 'exceptions' => true, 'trace' => 1)
                );
                switch ($_c) {
                    case 0 : $xmlStr = $client->$funcName();
                        break;
                    case 1 : $xmlStr = $client->$funcName($params[0]);
                        break;
                    case 2 : $xmlStr = $client->$funcName($params[0], $params[1]);
                        break;
                    case 3 : $xmlStr = $client->$funcName($params[0], $params[1], $params[2]);
                        break;
                    case 4 : $xmlStr = $client->$funcName($params[0], $params[1], $params[2], $params[3]);
                        break;
                    case 5 : $xmlStr = $client->$funcName($params[0], $params[1], $params[2], $params[3], $params[4]);
                        break;
                    case 6 : $xmlStr = $client->$funcName($params[0], $params[1], $params[2], $params[3], $params[4], $params[5]);
                        break;
                    default: $xmlStr = '';
                        break;
                }
            } catch (SoapFault $e) {
                //TODO SOAP有问题可以在这里调试 -zx
                print "Sorry an error was caught executing your request: {$e->getMessage()}";
                $xmlStr = '';
            }
            $aRet = xml_unserialize($xmlStr);
        } else {
            $aRet = self::soap($funcName, array(), $params);
        }

        self::_initSess(true);
        return $aRet;
    }

    //SOAP函数
    private static function soap($op, $head, $body) {
        $fp = fsockopen((self::$scheme . self::$host), self::$port, $errno, $errstr, self::$stamp);
        $return = array(
            'flag' => 0,
            'message' => 'Connect to host error！'
        );
        if ($fp) {
            $_xml = '';
            $_xml .= '<?xml version="1.0" encoding="utf-8"?>';
            $_xml .= '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">';
            $_xml .= '<soap:Header>';
            $_xml .= '<UserNameToken xmlns="http://tempuri.org/">';
            if (is_array($head)) {
                foreach ($head as $key => $value)
                    $_xml .= '<' . $key . '>' . $value . '</' . $key . '>';
            }
            $_xml .= '</UserNameToken>';
            $_xml .= '</soap:Header>';
            $_xml .= '<soap:Body>';
            $_xml .= '<' . $op . ' xmlns="http://tempuri.org/">';
            if (is_array($body)) {
                foreach ($body as $key => $value)
                    $_xml .= '<' . $key . '>' . $value . '</' . $key . '>';
            }
            $_xml .= '</' . $op . '>';
            $_xml .= '</soap:Body>';
            $_xml .= '</soap:Envelope>';

            $_msg = "POST " . self::$cgi . " HTTP/1.1\r\n";
            $_msg .= "Host: " . self::$host . "\r\n";
            $_msg .= "Content-Type: text/xml; charset=utf-8\r\n";
            $_msg .= "Content-Length: " . strlen($_xml) . "\r\n";
            $_msg .= "Connection: Close \r\n";
            $_msg .= "SOAPAction: \"http://tempuri.org/{$op}\"\r\n\r\n";
            $_rs = '';
            if (false !== fwrite($fp, ($_msg . $_xml))) {
                while (!feof($fp)) {
                    $_rs .= fgets($fp, 1024);
                }
            }
            fclose($fp);

            $_rs = htmlspecialchars_decode($_rs);
            if (!empty($_rs)) {
                preg_match("/<.{1,4}({$op}Response)\s*?.*?><return xsi:type=[^>]{4,20}>(.+?)<\/return><\/.{1,4}\\1>/", $_rs, $_matches);
                $return = (xml_unserialize($_matches[2]));
            }
        }
        return $return;
    }

    // </editor-fold>
    // </editor-fold>
}

//if ($_siteSurfix == 'com') {
//	CnvpClientSSO::setScheme('ssl://');
//	CnvpClientSSO::setPort(443);
//} else {
//	CnvpClientSSO::setScheme('');
//	CnvpClientSSO::setPort(80);
//}
//
CnvpClientSSO::setHost(_SOAP_HOST_);
CnvpClientSSO::setCGI(_SOAP_GI_);
CnvpClientSSO::setSite(_SOAP_SITE_);
?>